package com.jie.user.service;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jie.common.constant.RedisKey;
import com.jie.common.util.ApiRestrictUtil;
import com.jie.common.util.RandomUtil;
import com.jie.component.bloomfilter.core.BloomFilterManager;
import com.jie.component.exception.base.BusinessException;
import com.jie.component.exception.enums.BusinessError;
import com.jie.component.id.toolkit.SnowflakeIdGenerator;
import com.jie.component.lock.annotion.ServiceLock;
import com.jie.component.lock.core.LockType;
import com.jie.model.dto.user.UserRegisterWithPhoneDTO;
import com.jie.model.dto.user.UserRegisterWithPhoneSendCodeDTO;
import com.jie.model.po.user.User;
import com.jie.model.po.user.UserPhone;
import com.jie.user.mapper.UserMapper;
import com.jie.user.mapper.UserPhoneMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

import static com.jie.common.constant.LockKey.REGISTER_USER_WITH_PHONE_LOCK;
import static com.jie.common.constant.NumConst.ENABLE;
import static com.jie.common.constant.RedisKey.USER_REGISTER_PHONE_SEND_CODE;
import static com.jie.common.constant.StrConst.BLOOM_FILTER_USER_REGISTER_PHONE;

/**
 * @author jie
 * @version 1.0 2025-02-13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserRegisterService {

    private final SnowflakeIdGenerator snowflakeIdGenerator;

    private final BloomFilterManager bloomFilterManager;

    private final UserMapper userMapper;

    private final UserPhoneMapper userPhoneMapper;

    private ApiRestrictUtil registerRestrictUtil;

    private final StringRedisTemplate stringRedisTemplate;

    public void setApiRestrictUtil(ApiRestrictUtil apiRestrictUtil) {
        this.registerRestrictUtil = apiRestrictUtil;
    }

    @Transactional(rollbackFor = Exception.class)
    @ServiceLock(lockType = LockType.Write, name = REGISTER_USER_WITH_PHONE_LOCK, keys = {"#userRegisterWithPhoneDTO.phone"})
    public void registerWithPhone(UserRegisterWithPhoneDTO userRegisterWithPhoneDTO) {
        // 1. 请求限流检测
        if (registerRestrictUtil.onRequest()) {
            throw new BusinessException(BusinessError.SYSTEM_BUSY);
        }

        String phone = userRegisterWithPhoneDTO.getPhone();

        // 2. 校验验证码，从redis中获取验证码
        String key = RedisKey.getRedisKey(USER_REGISTER_PHONE_SEND_CODE + phone);
        String code = stringRedisTemplate.opsForValue().get(key);

        if (StrUtil.isBlank(code)) {
            throw new BusinessException(BusinessError.CODE_HAS_EXPIRE);
        }

        if (!code.equals(userRegisterWithPhoneDTO.getCode())) {
            throw new BusinessException(BusinessError.CODE_ERROR);
        }

        // 3. 布隆过滤器判断手机号是否注册（布隆过滤器特点，在里面的，不一定在，不在里面的，一定不在）
        boolean isRegister = bloomFilterManager.contains(BLOOM_FILTER_USER_REGISTER_PHONE, phone);
        if (isRegister) {
            LambdaQueryWrapper<UserPhone> queryWrapper = new LambdaQueryWrapper<>();
            // select * from user_phone where phone = ?
            queryWrapper.eq(UserPhone::getPhone, phone);
            isRegister = userPhoneMapper.selectCount(queryWrapper) > 0;
        }

        if (isRegister) {
            throw new BusinessException(BusinessError.PHONE_REGISTER);
        }
        // 4. 注册用户
        User user = User
                .builder()
                .id(snowflakeIdGenerator.nextId())
                .name(RandomUtil.randomUsername())
                .phone(phone)
                .password(userRegisterWithPhoneDTO.getPassword())
                .status(ENABLE)
                .build();

        UserPhone userPhone = UserPhone
                .builder()
                .id(snowflakeIdGenerator.nextId())
                .userId(user.getId())
                .phone(phone)
                .status(ENABLE)
                .build();

        userMapper.insert(user);
        userPhoneMapper.insert(userPhone);

        // 5. 添加布隆过滤器
        bloomFilterManager.add(BLOOM_FILTER_USER_REGISTER_PHONE, phone);
    }

    /**
     * 用户注册发送验证码
     * @param userRegisterWithPhoneSendCodeDTO
     */
    public void sendCode(UserRegisterWithPhoneSendCodeDTO userRegisterWithPhoneSendCodeDTO) {
        String phone = userRegisterWithPhoneSendCodeDTO.getPhone();
        String key = RedisKey.getRedisKey(USER_REGISTER_PHONE_SEND_CODE + phone);

        // 使用Redis的setIfAbsent（即setnx）命令，确保原子性
        String code = RandomUtil.randomCode(6);
        Boolean setSuccess = stringRedisTemplate.opsForValue()
                .setIfAbsent(key, code, 1, TimeUnit.MINUTES); // 直接设置过期时间，保证原子性

        if (Boolean.FALSE.equals(setSuccess)) {
            throw new BusinessException(BusinessError.CODE_HAS_BEEN_SENT);
        }

        log.info("发送验证码 {}", code);
    }
}
